package testApplets;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;

import marchingCubes.IsMCVertex;
import marchingCubes.MCube;

import simpleGeom.BinarySaver;
import simpleGeom.Box3D;
import simpleGeom.FloatGrid3D;
import testApplets.FinalMeshCutApplet.Cell;

public class VoxelStream {
	String path;
	Box3D bounds;
	float dim;
	int nX;
	int nY;
	int nZ;
	float[][] values;
	int startI;// current lowest Z index
	int nBuffer = 10;
	
	public VoxelStream(String path){
		this.path=path;
	}
	
	public VoxelStream(String path,float dim, float sX,float sY,float sZ,int nX,int nY,int nZ){
		this.path=path;
		this.dim=dim;
		this.nX=nX;
		this.nY=nY;
		this.nZ=nZ;
		bounds=new Box3D(sX,sY,sZ,nX*dim,nY*dim,nZ*dim);
		
	}
	
	public void initBuffer(int nBuffer){
		this.nBuffer=nBuffer;
		values=new float[nBuffer][nX*nZ];
		System.out.println("init Buffer: "+nBuffer+" l: "+(nX*nZ));
	}
	public void init(int nY){
		float[] fillings=new float[nX*nZ];
		System.out.println(nX*nZ);
		Arrays.fill(fillings, 1);
		for (int i=0;i<nY;i++){
			System.out.println(i);
			saveValuesBinary(fillings,i);
		}
	}
	public Cell[][][] getCellGrid(int yStart,int yEnd,int step){
		int nY=(yEnd-yStart);
		startI=yStart;
		initBuffer(1);
		this.dim*=step;
		loadAllValues();
		int gridNX=nX/step;
		int gridNY=nY/step;
		int gridNZ=nZ/step;
		Cell[][][]grid=new Cell[gridNX][gridNY][gridNZ];
		for (int y = 0; y < gridNY; y ++) {
			this.startI =  y*step+yStart;
			this.loadAllValues();
			System.out.println("y: " + y);
			for (int x = 0; x < gridNX; x ++) {
				for (int z = 0; z < gridNZ; z ++) {
					Cell cell = new Cell(x, y, z);
					cell.setValue(getValue(x*step, y*step+yStart, z*step));
					grid[x][y][z] = cell;
				}
			}
		}
		return grid;
	}

	public float[][][] getValueGrid(int yStart, int yEnd) {
		int nY = yEnd - yStart;
		startI = yStart;
		initBuffer(nY);
		loadAllValues();
		float[][][] grid = new float[nX][nY][nZ];
		for (int x = 0; x < nX; x++) {
			for (int z = 0; z < nZ; z++) {
				for (int y = yStart; y < yEnd; y++) {
					grid[x][y - yStart][z] = getValue(x, y, z);
				}
			}
		}
		return grid;
	}
	public  void fillValueGrid(int yStart, int yEnd,float[][][]grid) {
		int nY = yEnd - yStart;
		startI = yStart;
		for (int x = 0; x < nX; x++) {
			for (int z = 0; z < nZ; z++) {
				for (int y = yStart; y < yEnd; y++) {
					grid[x][y - yStart][z] = getValue(x, y, z);
				}
			}
		}
	}
	public  void fillValueGrid(int yStart, int yEnd,FloatGrid3D grid) {
		int nY = yEnd - yStart;
		startI = yStart;
		for (int x = 0; x < nX; x++) {
			for (int z = 0; z < nZ; z++) {
				for (int y = yStart; y < yEnd; y++) {
					grid.set(x, y-yStart, z, getValue(x, y, z));
				}
			}
		}
	}
	
	public  void fillValueGrid(int yStart, int yEnd,float[][][]grid,int step) {
		int nY = yEnd - yStart;
		startI = yStart;
		for (int x = 0; x < grid.length; x++) {
			for (int z = 0; z < grid[0][0].length; z++) {
				for (int y = 0; y < grid[0].length; y++) {
					grid[x][y][z] = getValue(x*step, (y*step)+yStart, z*step);
				}
			}
		}
	}
	public  void fillValueGrid(int yStart, int yEnd,FloatGrid3D grid,int step) {
		int nY = yEnd - yStart;
		startI = yStart;
		for (int x = 0; x <grid.nX; x++) {
			for (int z = 0; z < grid.nZ; z++) {
				for (int y = 0; y < grid.nY; y++) {
					grid.set(x, y, z, getValue(x*step, (y*step)+yStart, z*step));
				}
			}
		}
	}
	public Cell[][][] getCellGrid(int yStart,int yEnd){
		int nY=yEnd-yStart;
		startI=yStart;
		initBuffer(nY);
		loadAllValues();
		
		Cell[][][]grid=new Cell[nX][nY][nZ];
		for (int x=0;x<nX;x++){
			for (int z=0;z<nZ;z++){
			for (int y=yStart;y<yEnd;y++){
				Cell cell = new Cell(x, y, z);
				cell.setValue(getValue(x,y,z));
				grid[x][y-yStart][z] = cell;
			}
		}
		}
		return grid;
	}
	
	
	public void saveAllValues() {
		for (int i = 0; i < nBuffer; i++) {
			int cI = startI + i;
			if (cI>=0&&cI<nY){
				saveValuesBinary(values[i], cI);
			}
		}
		System.out.println("all Saved");
	}

	public void loadAllValues() {
		for (int i = 0; i < nBuffer; i++) {
			//System.out.println("load i"+i);
			int cI = startI + i;
			if (cI>=0&&cI<nY){
				//System.out.println("cI"+cI);
				loadValuesBinary(cI, values[i]);
			}
		}
	}
	public void loadValuesBinary(int sourceI, float[] target) {
		
		BinarySaver.readFloatArray(getPath(sourceI), target);
		
	}
	/*public void loadValues(int sourceI, float[] target) {
		String file = getPath(sourceI);
		
		try {
			BufferedReader in = new BufferedReader(new FileReader(file));
			int iLine = 0;
			String cString = null;
			while ((cString = in.readLine()) != null) {
				target[iLine] = Float.valueOf(cString);
				iLine++;
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}*/

	public float getValue(int x, int y, int z) {
		return values[y-startI][getIndex(x, z)];
	}

	public void setValue(int x, int y, int z, float value) {
		values[y-startI][getIndex(x, z)] = value;
	}

	public int getIndex(int x, int z) {
		return x * nZ + z;
	}

	public String getPath(int i) {
		return path + "/" + (i +10000)+ ".dat";
	}
	public void saveValuesBinary(float[] source, int targetI){
		String fileName=getPath(targetI);
		BinarySaver.writeFloatArray(source, fileName);
	}
	
	/*public void saveValues(float[] source, int targetI) {
		try {
			FileWriter fw = new FileWriter(getPath(targetI));
			
			BufferedWriter bw = new BufferedWriter(fw);
			int endI = source.length - 1;
			for (int i = 0; i < endI; i++) {
				bw.write("" + source[i]);
				bw.newLine();
			}
			bw.write("" + source[source.length - 1]);
			bw.flush();
			bw.close();
			fw.close();
			// fileOutputStream.close();
		} catch (Exception e) {
			
			System.err.println("error writing file: "+e);
		}
	}*/
	class Cell implements IsMCVertex {
		short x, y, z;
		float d = Float.MAX_VALUE;
		int id;
		MCube cube;
		Cell(int x, int y, int z) {
			this.x = (short) x;
			this.y = (short) y;
			this.z = (short) z;
		}

		public int getXK() {
			return x;
		}

		public int getYK() {
			return y;
		}

		public int getZK() {
			return z;
		}

		@Override
		public float getX() {
			// TODO Auto-generated method stub
			// return nX%3;
			//x * dim + bounds.x1
			return x ;
			// return x;
		}

		@Override
		public float getY() {
			// TODO Auto-generated method stub
			return y ;
			// return y;
		}

		public float getZ() {
			// TODO Auto-generated method stub
			return z ;
			// return z;
		}

		public float getValue() {
			return d;
		}

		public void setValue(float value) {
			this.d = value;
		}

		@Override
		public void setPosition(float x, float y, float z) {
			// TODO Auto-generated method stub

		}

		@Override
		public void setPosition(float x, float y) {
			// TODO Auto-generated method stub

		}

		@Override
		public int getId() {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public void setId(int i) {
			// TODO Auto-generated method stub

		}

		@Override
		public MCube getCube() {
			return cube;
		}

		@Override
		public void setCube(MCube cube) {
			// TODO Auto-generated method stub
			this.cube=cube;
		}

		@Override
		public void setZ(float z) {
			// TODO Auto-generated method stub

		}

		@Override
		public void setX(float x) {
			// TODO Auto-generated method stub

		}

		@Override
		public void setY(float Y) {
			// TODO Auto-generated method stub

		}

	}

}
